/**
 * 
 */
package tileStyle;

import Prism.core.Architecture;
import Prism.core.Event;
import Prism.core.FIFOScheduler;
import Prism.core.PrismConstants;
import Prism.core.RRobinDispatcher;
import Prism.core.Scaffold;
import Prism.extensions.port.ExtensiblePort;
import Prism.extensions.port.distribution.SocketDistribution;
import tileStyle.Prism.NodeAddress;
import tileStyle.Prism.events.*;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * @author Yuriy
 *
 */
public class SignInClient extends TileStyleComponent {

	public static final long serialVersionUID = 1L;
	
	protected Architecture architecture;
//	protected Scaffold scaffold;
	protected SocketDistribution sd;
	protected FIFOScheduler scheduler;
	protected RRobinDispatcher dispatcher;
	
	protected ExtensiblePort outPort;
	protected ExtensiblePort inPort;
	
	protected boolean everyoneSet;
	
	protected String signInHostname, myHostname;
	Map<TileType, List<NodeAddress>> addresses;
	
	public SignInClient(String myHostname, String signInHostname) {
		super("Sign In Client on: " + myHostname);
		this.signInHostname = signInHostname;
		this.myHostname = myHostname;
		everyoneSet = false;
		initializeArchitecture(2600);
		
		// wait for the server to become available
		boolean serverReady = false; 

		System.out.println("Trying to sign into " + signInHostname);
		while (!(serverReady)) {
			try {
				serverReady = true;
				outPort.connect(signInHostname, 2600);
			}
			catch (IOException e) {
				serverReady = false;
			}
		}
		
		// sign into the server
		send(new SignInEvent("Signing in from " +  myHostname, myHostname), outPort);
	}
	
	private void initializeArchitecture(int portNum) {
		architecture = new Architecture("SignIn Client");
		scheduler = new FIFOScheduler();
		Scaffold scaff = new Scaffold();
		dispatcher = new RRobinDispatcher(scheduler, 100);
		
		scaff.dispatcher = dispatcher;
		scaff.scheduler = scheduler;
		scaffold = scaff;

		architecture.scaffold = scaffold;
		
		outPort = new ExtensiblePort("outPort", PrismConstants.REQUEST);
		outPort.scaffold = scaffold;
		SocketDistribution sdOut = new SocketDistribution(outPort);   
		outPort.addDistributionModule(sdOut);
		this.addCompPort(outPort);
		architecture.add(outPort);
		
		inPort = new ExtensiblePort("inPort" + portNum, PrismConstants.REPLY);
		sd = new SocketDistribution(inPort, portNum);
		inPort.addDistributionModule(sd);
		inPort.scaffold = scaffold;
		this.addCompPort(inPort);
		architecture.add(inPort);
		architecture.add(this);

		
		dispatcher.start();
		architecture.start();
	}
	
	public synchronized void handle(Event event) {
		if (TileStyleStarter.DEBUG)
			System.out.println("handling event of type " + event.getClass());
		if (event instanceof AddressMapEvent) {
			AddressMapEvent e = (AddressMapEvent) event;

			addresses = e.getAddresses();
			notifyAll();
		}
		else if (event instanceof EveryoneSetEvent) {
			everyoneSet = true;
		}
		else if (event instanceof KeepAlive) {
			// do nothing
		}
		else throw new TileStyleException("Unknown event type" + event.getClass());
	}
	
	public boolean isReady() {
		return (addresses != null);
	}
	
	public Map<TileType, List<NodeAddress>> getAddresses() {
		return addresses;
	}
	
	public void set() {
		send(new ClientSetEvent("Client " + myHostname + " is set", myHostname), outPort);
	}

	public boolean isEveryoneSet() {
		return everyoneSet;
	}
	
	public void go() {
		send(new ClientGoEvent("Client " + myHostname + " is go", myHostname), outPort);
	}
}
